
/*
**	Syncvar module by Henry Tran.
**	GNU GENERAL PUBLIC LICENSE V3
**	Might be dumb to license a garry's mod module, but who cares.
*/

/*
**	Enum
*/

SYNCVAR_SHORT = 1
SYNCVAR_LONG = 2
SYNCVAR_FLOAT = 3
SYNCVAR_STRING = 4
SYNCVAR_VECTOR = 5
SYNCVAR_ENTITY = 6
SYNCVAR_BOOL = 7

if SERVER then
require "umsg"
else
require "usermessage"
end

local umsg, table, usermessage, _G, pairs, MsgN, SERVER, CLIENT, type, ValidEntity = umsg, table, usermessage, _G, pairs, MsgN, SERVER, CLIENT, type, ValidEntity

module "syncvar"

local syncvar_table = {}
local syncvar_reverse = {}
local syncvar_vars = {}
local syncvar_callbacks = {}

local syncvar_table_ent = {}
local syncvar_reverse_ent = {}
local syncvar_vars_ent = {}
local syncvar_callbacks_ent = {}

local functions
if SERVER then
	functions = {
		[_G.SYNCVAR_SHORT] = umsg.Short,
		[_G.SYNCVAR_LONG] = umsg.Long,
		[_G.SYNCVAR_FLOAT] = umsg.Float,
		[_G.SYNCVAR_STRING] = umsg.String,
		[_G.SYNCVAR_VECTOR] = umsg.Vector,
		[_G.SYNCVAR_ENTITY] = umsg.Entity,
		[_G.SYNCVAR_BOOL] = umsg.Bool
	}
else
	functions = {
		[_G.SYNCVAR_SHORT] = "ReadShort",
		[_G.SYNCVAR_LONG] = "ReadLong",
		[_G.SYNCVAR_FLOAT] = "ReadFloat",
		[_G.SYNCVAR_STRING] = "ReadString",
		[_G.SYNCVAR_VECTOR] = "ReadVector",
		[_G.SYNCVAR_ENTITY] = "ReadEntity",
		[_G.SYNCVAR_BOOL] = "ReadBool"
	}
end

local types = {
	[_G.SYNCVAR_SHORT] = "number",
	[_G.SYNCVAR_LONG] = "number",
	[_G.SYNCVAR_FLOAT] = "number",
	[_G.SYNCVAR_STRING] = "string",
	[_G.SYNCVAR_VECTOR] = "vector",
	[_G.SYNCVAR_ENTITY] = "entity",
	[_G.SYNCVAR_BOOL] = "boolean"
}

function GetTable()
	return syncvar_vars_ent
end

function GetRegTable()
	return syncvar_table_ent
end

function RegisterEntity( name, type, islocal )
	syncvar_reverse_ent[ name ] = table.insert( syncvar_table_ent, { name, type, islocal } )
end

function Register( name, type )
	syncvar_reverse[ name ] = table.insert( syncvar_table, { name, type } )
end

function SetEntity( entity, name, var )
	if not ValidEntity( entity ) or not syncvar_reverse_ent[ name ] or (not ( var == false ) and not var) then return end
	syncvar_vars_ent[ entity ] = syncvar_vars_ent[ entity ] or {}
	syncvar_vars_ent[ entity ][ name ] = var
	if CLIENT then return end

	if not functions[syncvar_table_ent[syncvar_reverse_ent[ name ]][2]] then return end

	if syncvar_table_ent[syncvar_reverse_ent[ name ]][3] then
		umsg.Start( "syncvar_ent", entity )
	else
		umsg.Start( "syncvar_ent" )
	end
		umsg.Short( syncvar_reverse_ent[ name ] )
		umsg.Entity( entity )
		functions[syncvar_table_ent[syncvar_reverse_ent[ name ]][2]]( var )
	umsg.End()
end

function Set( name, var )
	if not syncvar_reverse[ name ] or (not ( var == false ) and not var) then return end
	syncvar_vars[ name ] = var

	if CLIENT then return end

	if not functions[syncvar_table[syncvar_reverse[ name ]][2]] then return end

	umsg.Start( "syncvar_set" )
		umsg.Short( syncvar_reverse[ name ] )
		functions[syncvar_table[syncvar_reverse[ name ]][2]]( var )
	umsg.End()
end

function SyncPlayer( pl )
	for i,data in pairs( syncvar_table ) do
		umsg.Start( "syncvar_set", pl )
			umsg.Short( i )
			functions[ data[2] ]( syncvar_vars[ data[1] ] )
		umsg.End()
	end
	for ent,info in pairs( syncvar_vars_ent ) do
		for name,var in pairs( info ) do
			umsg.Start( "syncvar_ent", pl )
				umsg.Short( syncvar_reverse_ent[name] )
				umsg.Entity( ent )
				functions[ syncvar_table_ent[syncvar_reverse_ent[name]][2] ]( var )
			umsg.End()
		end
	end
end

function Get( name, default )
	return syncvar_vars[ name ] or default
end

function GetEntity( entity, name, default )
	if not ValidEntity(entity ) or not syncvar_vars_ent[ entity ] then return default end
	return syncvar_vars_ent[ entity ][ name ] or default
end

if CLIENT then

local id , entity , read_func , var
local function umsgCallbackEnt ( um )
	id = um:ReadShort()
	entity = um:ReadEntity()
	if not syncvar_table_ent[id] or not ValidEntity( entity ) then return end
	read_func = um[functions[syncvar_table_ent[id][2]]]

	if not read_func then return end

	var = read_func( um )

	syncvar_vars_ent[ entity ] = syncvar_vars_ent[ entity ] or {}
	syncvar_vars_ent[ entity ][ syncvar_table_ent[id][1] ] = var

	if not syncvar_callbacks_ent[ syncvar_table_ent[id][1] ] or not type( syncvar_callbacks_ent[ syncvar_table_ent[id][1] ] ) == "function" then return end
	syncvar_callbacks_ent[ syncvar_table_ent[id][1] ]( entity, var )
end
usermessage.Hook( "syncvar_ent", umsgCallbackEnt )

local function umsgCallback ( um )
	id = um:ReadShort()
	if not syncvar_table[id] then return end
	read_func = um[functions[syncvar_table[id][2]]]
	if not read_func then return end
	var = read_func( um )
	syncvar_vars[ syncvar_table[id][1] ] = var
	if not syncvar_callbacks[ syncvar_table[id][1] ] or not type( syncvar_callbacks[ syncvar_table[id][1] ] ) == "function" then return end
	syncvar_callbacks[ syncvar_table[id][1] ]( var )
end
usermessage.Hook( "syncvar_set", umsgCallback )

function SetCallbackEntity( name, funct )
	syncvar_callbacks_ent[name] = funct
end

function SetCallback( name, funct )
	syncvar_callbacks[name] = funct
end
end

